package com.meizhao.admin.service;

import com.meizhao.admin.configuration.ConfigProperties;
import com.meizhao.admin.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;


@Slf4j
@Service
public class BackUpService {

    private Environment env;

    private ThreadPoolTaskExecutor executor;

    private ConfigProperties configProperties;

    @Autowired
    public BackUpService(Environment env, ThreadPoolTaskExecutor executor, ConfigProperties configProperties) {
        this.env = env;
        this.executor = executor;
        this.configProperties = configProperties;
    }

    private StringBuffer getDbBackUpCommand(){

        return new StringBuffer().append("mysqldump -u").append(configProperties.getUsername())
                .append(" -p").append(configProperties.getPassword()).append(" -h").append(configProperties.getHost())
                .append(" -P").append(configProperties.getPort()).append(" -x ").append(configProperties.getDb())
                .append(" > ");
    }

    private String[] getExportCommand(String perfix){

        if(env.getProperty("os.name").contains("Windows")){

            File file = new File(configProperties.getDbBackupWinPath());
            if(!file.exists()){
                file.mkdir();
            }
            return new String[] {"cmd","/c", getDbBackUpCommand()
                    .append(configProperties.getDbBackupWinPath()).append(perfix).append("_db.sql").append(" -N ").append(" -C ")
                    .append(" --skip-comments ").append(" --opt ")
                    .append(" --default-character-set=utf8 ").toString()};
        }else{
            File file = new File(configProperties.getDbBackupLinuxPath());
            if(!file.exists()){
                file.mkdir();
            }
            return new String[] {"/bin/sh","-c", getDbBackUpCommand()
                    .append(configProperties.getDbBackupLinuxPath()).append(perfix).append("_db.sql").append(" -N ").append(" -C ")
                    .append(" --skip-comments ").append(" --opt ")
                    .append(" --default-character-set=utf8 ").toString()};
        }
    }

    private String[] getExportCommand(){

        return getExportCommand(DateUtils.timeStamp2Date(System.currentTimeMillis(), null));
    }


    public void backupDB(){

        System.out.println("start backupDB...");
        String[] exportCommand = getExportCommand();
        log.info(Arrays.toString(exportCommand));
        try {
            ProcessBuilder pbuilder = new ProcessBuilder(exportCommand);
            pbuilder.redirectErrorStream(true);
            Process process = pbuilder.start();
            executor.execute(() -> {
                try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))){
                    String str1 = null;
                    while ((str1 = outputReader.readLine()) != null) {
                        log.info("备份数据库命令输出: {}", str1);
                    }
                    log.info("backupDB success");
                    deleteRedundantDbFile();
                }catch (IOException e){
                    log.info("备份数据库命令输出异常");
                }
            });
        } catch (Exception e) {
            System.out.println("export db error");
            e.printStackTrace();
        }
    }

    private void deleteRedundantDbFile(){

        String exportPath;
        if(env.getProperty("os.name").contains("Windows")) {
            exportPath = configProperties.getDbBackupWinPath();
        }else{
            exportPath = configProperties.getDbBackupLinuxPath();
        }
        File file = FileUtils.getFile(exportPath);
        if(file != null && file.isDirectory()){
            String[] fileList = file.list();
            if(fileList != null && fileList.length > 0){
                Arrays.parallelSort(fileList);
                String fileName = fileList[fileList.length-1];
                File file2 = FileUtils.getFile(exportPath + fileName);
                if(file2 != null){
                    try{
                        if(fileList.length > configProperties.getRemainBackDbSize()){
                            for(int i = 0; i < fileList.length - configProperties.getRemainBackDbSize(); i++){
                                FileUtils.forceDelete(FileUtils.getFile(exportPath + fileList[i]));
                            }
                            log.info("delete redundant dbfile success");
                        }
                    }catch (Exception e) {
                        log.error("delete redundant dbfile error");
                    }
                }
            }
        }

    }

    @Scheduled(cron = "${system.config.dbBackupRate}")
    public void cornBackUpDb(){
        backupDB();
    }

    public void exportDb(HttpServletRequest request, HttpServletResponse response) {

        String exportPath;
        if(env.getProperty("os.name").contains("Windows")) {
            exportPath = configProperties.getDbBackupWinPath();
        }else{
            exportPath = configProperties.getDbBackupLinuxPath();
        }
        String prefix = DateUtils.timeStamp2Date(System.currentTimeMillis()
                , null);
        String[] exportCommand = getExportCommand(prefix);
        log.info(Arrays.toString(exportCommand));
        try {
            ProcessBuilder pbuilder = new ProcessBuilder(exportCommand);
            pbuilder.redirectErrorStream(true);
            Process process = pbuilder.start();
            try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))){
                String str1 = null;
                while ((str1 = outputReader.readLine()) != null) {
                    log.info("备份数据库命令输出: {}", str1);
                }
                log.info("backupDB success");

                File file = new File(exportPath, prefix+"_db.sql");
                // 如果文件名存在，则进行下载
                if (file.exists()) {

                    // 配置文件下载
                    response.setHeader("content-type", "application/octet-stream");
                    response.setContentType("application/octet-stream");
                    // 下载文件能正常显示中文
                    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(prefix+"_db.sql", "UTF-8"));

                    // 实现文件下载
                    byte[] buffer = new byte[1024];
                    FileInputStream fis = null;
                    BufferedInputStream bis = null;
                    try {
                        fis = new FileInputStream(file);
                        bis = new BufferedInputStream(fis);
                        OutputStream os = response.getOutputStream();
                        int i = bis.read(buffer);
                        while (i != -1) {
                            os.write(buffer, 0, i);
                            i = bis.read(buffer);
                        }
                        log.info("Download dbfile successfully!");
                    }
                    catch (Exception e) {
                        log.info("Download dbfile failed!");
                    }
                    finally {
                        if (bis != null) {
                            try {
                                bis.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (fis != null) {
                            try {
                                fis.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }

            }catch (IOException e){
                log.info("备份数据库命令输出异常");
            }
        } catch (Exception e) {
            System.out.println("export db error");
            e.printStackTrace();
        }
    }

}
